package tfts

import "math"

var enableADScaling = true

type latency interface {
	react(*action) bool
	String() string
}

type callable struct {
	name     string
	ApDmg    string
	AdDmg    string
	FixedDmg string // 无加成伤害，比如基于敌方最大生命值
	Heal     string // 技能治疗，支持表达式如最大生命值 10*hp => 10%最大生命值
	Shield   string // 护盾，支持表达式如最大生命值
	Grow     string // 最大生命值成长，支持表达式如最大生命值
	buff     *buffMeta
	mana     int
	swing    int // 施法前摇，单位100ms
	stackBy  stackType
	times    int     // 多少次stackBy事件增加一层叠加伤害
	stackDmg float64 // 每层叠加伤害
	calls    []func(*Fight)
}

type action struct {
	latency
	callable
	attrMeta
	*Hook
	fight      *Fight
	passiveDmg string
	count      int
	validCond  []func(*Fight) bool
	handleCond []func(*Fight) bool
}

type onceR struct {
	num    int
	target int
}

type rangeR struct {
	current int
	left    int // 不包含左边界
	right   int // 包含右边界
}

type repeatR struct {
	current   int
	threshold int
	history   int
	max       int
}

type stackR struct {
	times   int // 触发多少次叠加一层
	current int
	stack   int
	max     int
	reset   bool // 是否在最大层时重置
	once    bool
}

func NewAction(f *Fight, attrs ...*attrMeta) *action {
	return newAction(attrs...).attach(f)
}

func newAction(attrs ...*attrMeta) *action {
	meta := &attrMeta{factor: 100, fighter: &fighter{}}
	for _, attr := range attrs {
		meta.Add(attr)
	}
	res := &action{
		attrMeta: *meta,
	}
	return res
}

func min(a any, b any) float64 {
	c := 0.0
	switch a.(type) {
	case int:
		c = float64(a.(int))
	case float64:
	default:
		panic("damage type error")
	}
	d := 0.0
	switch b.(type) {
	case int:
		d = float64(b.(int))
	case float64:
	default:
		panic("damage type error")
	}
	res := 0.0
	if c < d {
		res = c
	} else {
		res = d
	}
	if res < 0.0 {
		return 0
	}
	return res
}

func (o *onceR) react(a *action) bool {
	o.num++
	if o.num != o.target {
		return false
	}
	a.factor = 100 // 使得属性值生效
	return true
}

func (o *onceR) String() string {
	return "一次性"
}

func (r *rangeR) react(a *action) bool {
	r.current++
	a.factor = 0
	if r.current >= r.left && r.current < r.right {
		a.factor = 100
	}
	return false
}

func (r *rangeR) String() string {
	return "时间段"
}

func (r *repeatR) react(_ *action) bool {
	if r.history >= r.max {
		return false
	}
	r.current++
	if r.current%r.threshold == 0 {
		r.history++
		return true
	}
	return false
}

func (r *repeatR) String() string {
	return "重复"
}

func (s *stackR) react(a *action) bool {
	s.current++
	if s.current%s.times != 0 {
		return false
	}
	if s.reset && s.stack == s.max {
		s.stack = 0
	}
	if s.stack >= s.max && s.max > 0 {
		return false
	}
	if s.stack < s.max || s.max <= 0 {
		s.stack++
	}
	a.factor = s.stack * 100
	if s.stack == s.max && s.max > 0 {
		return true
	}
	if s.max == -1 || !s.once {
		return true
	}
	return false
}

func (s *stackR) String() string {
	return "可叠加"
}

func (c *callable) damage(champ *Fight) float64 {
	phy := champ.parseDamage(c.AdDmg) * champ.ad()
	// 以加成类型来配置伤害时需要乘以攻击力
	if enableADScaling && champ.dmgType == Physics && phy < 39 {
		phy = phy * champ.attack
	}
	dmg := phy + champ.parseDamage(c.ApDmg)*champ.AP() + champ.parseDamage(c.FixedDmg)

	times := 0
	switch c.stackBy {
	case NoStk:
	case CastStk:
		times = champ.CastTimes
	case AtkStk:
		times = champ.AtkTimes
	case AsStk:
		// 基于额外攻速
		times = int((champ.as() - 1) * 100)
	default:
		panic("unknown stack type")
	}
	if c.stackBy != NoStk && times >= c.times {
		stack := float64(times/c.times) * c.stackDmg
		if champ.dmgType == Magic {
			stack *= champ.AP()
		} else {
			stack *= champ.ad()
		}
		dmg += stack
	}

	dmg *= champ.amp()
	dmg *= champ.castAmp()
	dmg *= 1 + champ.uAmp()
	if champ.skillCrit() {
		dmg *= 1 + champ.critRate()*champ.critAmp()
	}
	return dmg
}

func (c *callable) defence(champ *Fight) {
	champ.recover0(c)
}

func (a *action) handle(e Event) {
	if a.Hook == nil || !a.match(e) {
		return
	}
	for _, cond := range a.handleCond {
		if !cond(a.fight) {
			return
		}
	}
	if a.latency == nil || a.react(a) {
		if a.buff != nil {
			a.buff.attach(a.fight)
		}
		dmg := a.damage(a.fight)
		dmg += a.fight.parseDamage(a.passiveDmg)
		if dmg > 0.01 {
			name := a.name
			if len(name) == 0 {
				name = "-"
			}
			TraceLog("%4.1f秒:[%s]触发伤害=%0.f\n", a.fight.Current(), name, dmg)
		}
		a.fight.record(dmg, PassiveS)
		a.defence(a.fight)
		a.count++
		for _, call := range a.calls {
			call(a.fight)
			TraceLog("%4.1f秒:[%s]触发%d次\n", a.fight.Current(), a.name, a.count)
		}
	}
}

func (a *action) ValidCondition(cond func(*Fight) bool) *action {
	a.validCond = append(a.validCond, cond)
	return a
}

func (a *action) HandleCondition(cond func(*Fight) bool) *action {
	a.handleCond = append(a.handleCond, cond)
	return a
}

func (a *action) valid() bool {
	for _, c := range a.validCond {
		if !c(a.fight) {
			return false
		}
	}
	return a.factor > 0
}

func (a *action) Buff(buff *buffMeta) *action {
	a.buff = buff
	return a
}

func (a *action) attach(f *Fight) *action {
	a.fight = f
	f.attrs = append(f.attrs, a)
	f.handlers = append(f.handlers, a)
	return a
}

func (a *action) Once(target int) *action {
	a.latency = &onceR{target: target}
	a.factor = 0
	return a
}

func (a *action) Range(left, right int) *action {
	a.latency = &rangeR{left: left, right: right}
	return a
}

func (a *action) Repeat(threshold int, maxRepeat ...int) *action {
	times := math.MaxInt
	if len(maxRepeat) > 0 {
		times = maxRepeat[0]
	}
	a.latency = &repeatR{threshold: threshold, max: times}
	return a
}

func (a *action) Stack(perStack int, maxStacks ...int) *action {
	limit := -1
	if len(maxStacks) > 0 {
		limit = maxStacks[0]
	}
	if limit > 0 {
		a.latency = &stackR{times: perStack, max: limit, once: true}
	} else {
		a.latency = &stackR{times: perStack, max: limit}
	}
	a.factor = 0 // 从0开始叠加
	return a
}

// 会在叠加到最大层数时重置
func (a *action) Cycle(perStack int, maxStacks ...int) *action {
	a.Stack(perStack, maxStacks...)
	a.latency.(*stackR).reset = true
	return a
}

func (a *action) RepeatCallStack(perStack int, maxStacks ...int) *action {
	limit := -1
	if len(maxStacks) > 0 {
		limit = maxStacks[0]
	}
	a.latency = &stackR{times: perStack, max: limit}
	a.factor = 0 // 从0开始叠加
	return a
}

func (a *action) On(e Event) *action {
	if a.Hook == nil {
		a.Hook = &Hook{event: e}
		return a
	}
	tail := a.Hook
	for ; tail.next != nil; tail = tail.next {
	}
	tail.next = &Hook{event: e}
	return a
}

func (a *action) DealDmg(name, dmg string) {
	a.name = name
	a.passiveDmg = dmg
}

func (a *action) RestoreMana(name string, mana int) {
	a.AddCall(name, func(f *Fight) {
		if f.casting || f.lockMana() {
			TraceLog("%4.1f秒:法力锁定中无法触发MPS\n", f.Current())
			return
		}
		restore := float64(mana) * f.manaAmp()
		f.mana += restore
		TraceLog("%4.1f秒:[%s]回复%.1f点法力,当前法力值%.1f\n", f.Current(), name, restore, f.mana)
	})
}

func (a *action) AddCall(name string, call func(*Fight)) {
	a.name = name
	a.calls = append(a.calls, call)
}

func (a *action) TraceAction(name string) {
	a.name = name
	a.calls = append(a.calls, func(fight *Fight) {})
}
